#ifndef DYNAMICVARIABLE_H
#define DYNAMICVARIABLE_H

#include <map>
#include <iostream>
#include <string>

typedef std::string String;


//#include <utils/String.h>

class DynamicVariable
{
public:
    DynamicVariable() {}
    virtual String getType() { return "Base class. This should not be instantiated. "; }      
      
};

template<typename T>
class GenericDV : public DynamicVariable
{
public:
    GenericDV() {}
    GenericDV(T& data) : m_Data(data) {}
    T&             getData() { return m_Data; }
    void           setData(T data) { m_Data = data; }
    virtual String getType() { return typeid(T).name()+String(" Variable"); }
    
protected:
    T m_Data;
};

class DynamicClass
{
    typedef std::map<String, DynamicVariable*> VarMap;
	typedef std::map<String, DynamicVariable*>::iterator VarMapIterator;
	
	//Dynamic String-keyed map of member variables
	VarMap m_VarMap;
	//One string defining the particular 'type' of this dynamic class
	String m_sType;

	//Internal function to find a member variable
	DynamicVariable* _findVar(String sVar)
	{
    	VarMapIterator itFoundVar = m_VarMap.find(sVar);
        if (itFoundVar == m_VarMap.end()) {
    		std::cerr << "Error in DynamicClass: There is no variable named '"
                        << sVar
    					<< "' within dynamic class '"
    					<< m_sType << "'" << std::endl;
    		//NOTE: This is really bad, but there's no choice when we want to
    		//      use references. And it should probably be a fatal error
    		//      anyway.
    		return NULL;
    	}
    	return itFoundVar->second;
    }
	
	DynamicClass() 		{}
	
public:
	
	//TODO: Check if these are needed and\or should be allowed
	DynamicClass(String sType) : m_sType(sType) 		{}
	
	//Copy constructor
	DynamicClass( const DynamicClass& sourceClass )
	{
        //Copy and allocate memory for each variable in sourceClass
		std::map<String, DynamicVariable*>::const_iterator itVar;
		
		for (itVar = sourceClass.m_VarMap.begin(); itVar != sourceClass.m_VarMap.end(); ++itVar) {
            const DynamicVariable& sourceVar = *itVar->second;
            
            DynamicVariable* v = new DynamicVariable(sourceVar);
            m_VarMap[itVar->first] = v;
        }
    }
	
	//TODO: Write assignment operator
	
	//Destructor
    virtual ~DynamicClass()
    {
		//Free the memory allocated for each variable
		VarMapIterator itVar;
		for (itVar = m_VarMap.begin(); itVar != m_VarMap.end(); ++itVar) {
            delete itVar->second;
		}
	}
	
	//Construct from data file
	//TODO: Implement
	void constructFromData() 		{}
	
	String setType(String sType) 		{ m_sType = sType; }
	String getType() 					{ return m_sType; }
	
	//Construct manually
	//Returns a reference to the new (un-initialised!) variable
	//TODO: Consider changing this to pass in an initial value
	template<typename T>//, bool T_bAsTemplateOnly=false>
	T& addVar(String sName)
	{
    	if (m_VarMap.find(sName) != m_VarMap.end()) {
    		std::cerr << "Error in DynamicClass::addVar: Can't add new \
                          variable, dynamic class of type `" << m_sType
    					<< "' already contains a member variable with the \
                        name `" << sName
    					<< "'" << std::endl;
    		return *((T*)0);
    	}
    	std::cout << "Added member variable `" << sName
    				<< "' of type `" << typeid(T).name()
    				<< "' to dynamic class of type `" << m_sType
    				<< "'" << std::endl;
    	
    	//Set up the information about the new member variable
    	GenericDV<T>* newDV = new GenericDV<T>();
    	m_VarMap[sName] = newDV;

    	return newDV->getData();
    }
	
	//Template function to get a reference to a member variable of a certain type
	//TODO: Add method of checking if this instance is only a 'template', in
	//      which case this method should not be available.
	template<typename T>
	T& getVar(String sVar)
	{
    	DynamicVariable* v = _findVar(sVar);
        
    	#ifdef DEBUG //If in debug mode, use RTTI to safely cast the variable to its supposedly known type
            GenericDV<T> testVar = dynamic_cast<GenericDV<T>*>(v);
    		if (testVar != NULL)
    			return testVar->getData();
    		else {
    			std::cerr << "Fatal mistake: Variable `" << sVar
    						<< "' in class of type `" << m_sType
    						<< "' is of type `" << v.type
    						<< "' and not of type `" << typeid(T).name()
    						<< "' as requested" << std::endl;
    			//TODO: Fail (gracefully)
    		}
    	#else //If in release mode, just cast it blindly and hope
    		return (static_cast<GenericDV<T>*>(v))->getData();
    	#endif
    }
	
};

#endif
